package com.fengye.security.security.service;

import com.fengye.security.domain.User;
import com.fengye.security.domain.vo.TokenVo;
import com.fengye.security.security.repository.RoleRepository;
import com.fengye.security.security.repository.UserRepository;
import com.fengye.security.utils.JwtUtil;
import com.fengye.security.utils.TotpUtil;
import lombok.RequiredArgsConstructor;
import lombok.val;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.util.List;
import java.util.Optional;

/**
 * @author fengyexjtu@126.com
 * @date 2022年05月24日 2:51 PM
 */
@Service(value = "userDetailsServiceImpl")
@RequiredArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {

    private final UserRepository userRepository;
    private final RoleRepository roleRepository;
    private final PasswordEncoder passwordEncoder;

    private final JwtUtil jwtUtil;

    private final TotpUtil totpUtil;

    @PreAuthorize("hasRole('ADMIN')")
    public List<User> list() {
        return userRepository.findAll();
    }

    /**
     * 根据用户名查找用户
     *
     * @param username 用户名
     * @return 用户
     */
    public Optional<User> findOptionalByUsername(String username) {
        return userRepository.findOptionalByUsername(username);
    }

    /**
     * MFA 登录
     *
     * @param user 用户
     * @return token 对
     */
    public TokenVo loginWithTotp(User user) {
        val toSave = user.withMfaKey(totpUtil.encodeKeyToString());
        val saved = saveUser(toSave);
        return login(saved);
    }


    /**
     * 保存用户
     *
     * @param user 用户
     * @return 保存后的用户
     */
    @Transactional
    public User saveUser(User user) {
        return userRepository.save(user);
    }

    /**
     * 根据用户电子邮件地址查找用户
     *
     * @param email 用户电子邮件地址
     * @return 用户
     */
    public Optional<User> findOptionalByEmail(String email) {
        return userRepository.findOptionalByEmail(email);
    }

    /**
     * 根据用户名和密码进行匹配检查
     *
     * @param username 用户名
     * @param password 明文密码
     * @return 用户
     */
    public Optional<User> findOptionalByUsernameAndPassword(String username, String password) {
        return findOptionalByUsername(username)
                .filter(user -> passwordEncoder.matches(password, user.getPassword()));
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return userRepository.findUserByUsername(username).orElseThrow(() -> new UsernameNotFoundException("用户名为找到"));
    }

    /**
     * 注册一个新用户
     *
     * @param user 用户实体
     * @return 保存后的对象
     */
    public User register(User user) {
        // TODO: 注册用户要先建立角色的关系
        // Role.builder().
        // return roleRepository.findOptionalByAuthority(ROLE_USER)
        //         .map(role -> {
        //             val userToSave = user
        //                     .withRoles(Set.of(ROLE_USER))
        //                     .withPassword(passwordEncoder.encode(user.getPassword()));
        //             return userRepository.save(userToSave);
        //         }).orElseThrow();
        return null;
    }

    /**
     * 判断用户名是否存在
     *
     * @param username 用户名
     * @return 存在与否
     */
    public boolean isUsernameExisted(String username) {
        return userRepository.countByUsername(username) > 0;
    }

    /**
     * 判断电邮地址是否存在
     *
     * @param email 电邮地址
     * @return 存在与否
     */
    public boolean isEmailExisted(String email) {
        return userRepository.countByEmail(email) > 0;
    }

    /**
     * 在编辑用户的场景下，判断电子邮件是否重复，需要规避用户本身的 email
     *
     * @param email    电邮地址
     * @param username 用户名
     * @return 存在与否
     */
    public boolean isEmailExistedAndUsernameIsNot(String email, String username) {
        return userRepository.countByEmailAndUsernameIsNot(email, username) > 0;
    }

    /**
     * 判断手机号是否存在
     *
     * @param mobile 手机号
     * @return 存在与否
     */
    public boolean isMobileExisted(String mobile) {
        return userRepository.countByMobile(mobile) > 0;
    }

    /**
     * 如果用户密码采用的是旧的编码算法，那么利用此方法可以升级编码
     *
     * @param user        用户
     * @param rawPassword 明文密码
     */
    @Transactional
    public void upgradePasswordEncodingIfNeeded(User user, String rawPassword) {
        if (passwordEncoder.upgradeEncoding(user.getPassword())) {
            userRepository.save(user.withPassword(passwordEncoder.encode(rawPassword)));
        }
    }

    /**
     * 根据 UserDetails 生成 accessToken 和 refreshToken
     *
     * @param userDetails 用户信息
     * @return token 对
     */
    public TokenVo login(UserDetails userDetails) {
        return new TokenVo(jwtUtil.createAccessToken(userDetails), jwtUtil.createRefreshToken(userDetails));
    }


    /**
     * 创建 Totp
     *
     * @param user 用户
     * @return Totp
     */
    public Optional<String> createTotp(User user) {
        return totpUtil.createTotp(user.getMfaKey());
    }
}
